<?php
/**
 * DMFramework
 * 
 * @license Copyright 2012 Dillen Meijboom under the Apache license version 2.0 (http://apache.org/licenses/LICENSE-2.0)
 * @author Dillen Meijboom <info@dillenm.nl>
 * @url http://dmframework.nl
 */

/**
 * The image-manupilation class makes it easy to manupilate images in PHP.
 * 
 * @license Copyright 2012 Dillen Meijboom under the Apache license version 2.0 (http://apache.org/licenses/LICENSE-2.0)
 * @author Dillen Meijboom <info@dillenm.nl>
 * @package System.Library
 */
class Dmf_Image
{
    private $_filename;
    
    private $_filetype;
    
    private $_width;
    
    private $_height;
    
    private $_framework;
    
    const GD = 1;
    
    const IMAGEMAGICK = 2;
    
    public function __construct( $filename )
    {
        if ( ! file_exists( $filename ) )
        {
            throw new Exception( 'Image file: "' . $filename . '" not found.' );
        }
        
        $this->_filename = $filename;
        $this->_content = file_get_contents( $filename );
        
        $types = array(
                1 => 'jpg',
                2 => 'gif',
                3 => 'png',
                4 => 'bmp',
                5 => 'tiff',
                6 => 'libm'
            );

        preg_match( '/\A(?:(\xff\xd8\xff)|(GIF8[79]a)|(\x89PNG\x0d\x0a)|(BM)|(\x49\x49(\x2a\x00|\x00\x4a))|(FORM.{4}ILBM))/', $this->_content, $match );
        
        if ( ! isset( $types[ count( $match ) - 1 ] ) )
        {
            throw new Exception( 'Filetype could not be calculated from file: "' . $filename . '".' );
        }
        
        $this->_filetype = $types[ count( $match ) - 1 ];
        $this->_framework = Dmf_Image::GD;
        list( $this->_width, $this->_height ) = getimagesize( $filename );
    }
    
    public function useFramework( $framework )
    {
        $this->_framework = $framework;
    }
    
    public function resize( $width, $height )
    {
        $image = $this->_fromString( $this->_content );
        list( $width, $height ) = $this->_getSize( $width, $height );
        
        switch( $this->_framework )
        {
            case self::GD:
                $background = imagecreatetruecolor( $width, $height );
                
                imagealphablending( $background, false );
                imagesavealpha( $background, true );
                
                imagecopyresampled( $background, $image, 0, 0, 0, 0, $width, $height, $this->_width, $this->_height );
                
                //Update width and height
                $this->_width = $width;
                $this->_height = $height;
                break;
            case self::IMAGEMAGICK:
                throw new Dmf_Exception_NotImplemented( 'Image resize with imagemagick is not implemented yet.' );
                break;
        }
        
        $this->saveResource( $background );
    }
    
    private function _getSize( $width, $height )
    {
        if ( ! is_numeric( $width ) && is_numeric( $height ) )
        {
            $width = $this->_width * ( $height / $this->_height );
        }
        
        if ( ! is_numeric( $height ) && is_numeric( $width ) )
        {
            $height = $this->_height * ( $width / $this->_width );
        }
        
        if ( ! is_numeric( $width ) && ! is_numeric( $height ) )
        {
            throw new Exception( 'Can not resize image by ratio-height and width please use ratio on one side.' );
        }
        
        return array( $width, $height );
    }
    
    private function _fromString( $content )
    {
        switch( $this->_framework )
        {
            case self::GD:
                return imagecreatefromstring( $content );
                break;
            case self::IMAGEMAGICK:
                $image = new Imagick;
                $file = tempnam( 'system/cache', 'Dmf_Image_Resource' );
                
                return $image->readimagefile( fopen( $file, 'rb' ) );
                
                unlink( $file );
                break;
        }
    }
    
    public function getMeta()
    {
        $file = tempnam( 'system/cache', 'Dmf_Image_Resource' );
        $this->save( $file );
        
        $meta = array();
        
        switch( $this->_filetype )
        {
            case 'jpg':
            case 'jpeg':
                $meta = exif_read_data( $file, 0, true );
                break;
            case 'png':
                $fp = fopen( $file, 'rb' );
                $sig = fread( $fp, 8 );
                
                if ( $sig != "\x89PNG\x0d\x0a\x1a\x0a" )
                {
                    fclose($fp);
                    return false;
                }

                while ( ! feof( $fp ) )
                {
                    $data = unpack( 'Nlength/a4type', fread( $fp, 8 ) );
                    
                    if ( $data['type'] == 'IEND' ) break;

                    if ( $data['type'] == 'tEXt' )
                    {
                        list( $key, $val ) = explode( "\0", fread( $fp, $data['length'] ) );
                        
                        $meta[ $key ] = $val;

                        fseek( $fp, 4, SEEK_CUR );
                    }
                    else
                    {
                        fseek( $fp, $data['length'] + 4, SEEK_CUR );
                    }
                }


                fclose( $fp );
                break;
        }
        
        return $meta;
    }
    
    public function saveResource( $resource )
    {
        ob_start();
        
        if ( $this->_framework == self::IMAGEMAGICK )
        {
            echo $resource;
        }
        
        switch( strtolower( $this->_filetype ) )
        {
            case 'jpg':
            case 'jpeg':
                if ( $this->_framework == self::GD ) imagejpeg( $resource, null, 100 );
                break;
            case 'png':
                if ( $this->_framework == self::GD ) imagepng( $resource, null, 0 );
                break;
            case 'gif':
                if ( $this->_framework == self::GD ) imagegif( $resource );
                break;
            case 'gd':
                if ( $this->_framework == self::GD ) imagegd( $resource );
                break;
            case 'wbmp':
                if ( $this->_framework == self::GD ) imagewbmp( $resource );
                break;
            case 'gd2':
                if ( $this->_framework == self::GD ) imagegd2( $resource );
                break;
        }
        
        $this->_content = ob_get_clean();
    }
    
    public function to( $extension )
    {
        ob_start();
        
        $image = $this->_fromString( $this->_content );
        
        switch( strtolower( $extension ) )
        {
            case 'jpg':
            case 'jpeg':
                if ( $this->_framework == self::GD ) imagejpeg( $image, null, 100 );
                if ( $this->_framework == self::IMAGEMAGICK ) $image->setImageFormat( 'jpeg' );
                break;
            case 'png':
                if ( $this->_framework == self::GD ) imagepng( $image, null, 0 );
                if ( $this->_framework == self::IMAGEMAGICK ) $image->setImageFormat( 'png' );
                break;
            case 'gif':
                if ( $this->_framework == self::GD ) imagegif( $image );
                if ( $this->_framework == self::IMAGEMAGICK ) $image->setImageFormat( 'gif' );
                break;
            case 'gd':
                if ( $this->_framework == self::GD ) imagegd( $image );
                break;
            case 'wbmp':
                if ( $this->_framework == self::GD ) imagewbmp( $image );
                break;
            case 'gd2':
                if ( $this->_framework == self::GD ) imagegd2( $image );
                break;
        }
        
        if ( $this->_framework == self::IMAGEMAGICK )
        {
            echo $image;
        }
        
        $this->_content = ob_get_clean();
    }
    
    public function save( $filename = null )
    {
        return @file_put_contents( ( $filename == null ) ? $this->_filename : $filename, $this->_content, LOCK_EX );
    }
    
    public function getContent()
    {
        return $this->_content;
    }
}